#! /usr/bin/env perl
##
## Copyright (C) by Argonne National Laboratory
##     See COPYRIGHT in top-level directory
##

# This file builds mpif.h from mpi.h.in

use warnings;

# Setup global variables

$build_io = 1;
$write_mpif = 1;

$debug = 0;

#feature variables
our $prototype_file_a = "../../../include/mpi.h.in";

# Global hashes used for definitions and to record the locations of the
# definitions.
%mpidef = ();
%mpidefFile = ();

# Process arguments
#
# Args
# -feature={logical,fint,subdecls,weak,bufptr}, separated by :, value given
# by =on or =off, eg
# -feature=logical=on:fint=off
# The feature names mean:
#    logical - Fortran logicals are converted to/from C
#    fint    - Fortran integers and C ints are different size (not implemented)
#    subdecls - Declarations for PC-Fortran compilers added
#    weak    - Use weak symbols
#    bufptr  - Check for MPI_BOTTOM as a special address.  This is
#              not needed if a POINTER declaration is available.
foreach $_ (@ARGV) {
    if (/-infile=(.*)/) {
        # Special arg to help with debugging
        $prototype_file_a = $1;
        $write_mpif = 0;
    }
    elsif (/-noromio/) { $build_io = 0; }
    elsif (/-debug/) {
        $debug = 1;
    }
    elsif (/-feature=(.*)/) {
        foreach $feature (split(/:/,$1)) {
            print STDERR "Processing feature $feature\n" if $debug;
            # Feature values are foo=on,off
            ($name,$value) = split(/=/,$feature);
            # Default if feature is selected is to enable it.
            if (!defined($value)) { $value = 1; }
            else {
                if ($value eq "on") { $value = 1; }
                elsif ($value eq "off") { $value = 0; }
            }
            # Set the variable based on the string
            $varname = "do_$name";
            $$varname = $value;
        }
    }
    elsif (/deffile=(.*)/) {
        $definition_file = $1;
    }
    else {
        print STDERR "Unrecognized argument $_\n";
    }
}

#
# Load any definition file
if ($definition_file) {
    require $definition_file;
}

$arg_string = join( ' ', @ARGV );


#
# --------------------------------------------------------------------------
# Create mpif.h.in from mpi.h
#
# Need to put this into a routine similar to the ReadInterface routine
# in the c++ version.  This will allow us to read both mpi_proto.h
# and mpio.h.in (or other files)

&ReadInterfaceForDefinitions( $prototype_file_a );
if ( -s "../../../mpi/romio/include/mpio.h.in" && $build_io) {
    %skipBlocks = ( 'HAVE_MPI_DARRAY_SUBARRAY' => 1,
                   'HAVE_MPI_INFO' => 1,
                    'MPICH' => 1 );
    &ReadInterfaceForDefinitions( "../../../mpi/romio/include/mpio.h.in" );
    %skipBlocks = ();
}
#
if ($write_mpif) {

    # The ONLY valid comment character for Fortran 77 is a C in column 1
    # For those Fortran compilers that support it (which is most at this point)
    # the top-level configure will replace the "C" in column 1 with "!"
    # (also in column 1)
    $cchar = "C";
    open ( MPIFFD, ">mpif.h.in.new" ) || die "Could not open mpif.h.in.new\n";


    # Now, write out the file
    # This first line makes sure that other tools know that this is a
    # Fortran file
    print MPIFFD "$cchar      \n";
    print MPIFFD "$cchar      Copyright (C) by Argonne National Laboratory\n";
    print MPIFFD "$cchar          See COPYRIGHT in top-level directory\n";
    print MPIFFD "$cchar      \n";
    print MPIFFD "$cchar      DO NOT EDIT\n";
    print MPIFFD "$cchar      This file created by buildiface $arg_string\n";
    print MPIFFD "$cchar      \n";
    #
    # Status elements
    my $status_size = $mpidef{MPI_F_STATUS_SIZE};
    my $idx_source = $mpidef{MPI_F_SOURCE} + 1;
    my $idx_tag = $mpidef{MPI_F_TAG} + 1;
    my $idx_error = $mpidef{MPI_F_ERROR} + 1;
    # FIXME: The offsets for the status elements are hardwired.  If they
    # change in mpi.h.in, they need to change here as well.
    print MPIFFD "       INTEGER MPI_SOURCE, MPI_TAG, MPI_ERROR\n";
    print MPIFFD "       PARAMETER (MPI_SOURCE=$idx_source,MPI_TAG=$idx_tag,MPI_ERROR=$idx_error)\n";
    print MPIFFD "       INTEGER MPI_STATUS_SIZE\n";
    print MPIFFD "       PARAMETER (MPI_STATUS_SIZE=$status_size)\n";
    # Temporary until configure handles these.  Define as arrays to keep
    # Fortran compilers from complaining excessively.
    print MPIFFD "       INTEGER MPI_STATUS_IGNORE(MPI_STATUS_SIZE)\n";
    print MPIFFD "       INTEGER MPI_STATUSES_IGNORE(MPI_STATUS_SIZE,1)\n";
    #
    # Other special constants.  ERRCODES_IGNORE and ARGVS_NULL
    # are both like STATUS(ES)_IGNORE
    print MPIFFD "       INTEGER MPI_ERRCODES_IGNORE(1)\n";
    print MPIFFD "       CHARACTER*1 MPI_ARGVS_NULL(1,1)\n";
    # Unfortunately, we cannot parameter initialize this.  Further,
    # there is no default initialization.  We could use a block data item...
    # ARGV_NULL can actually be a single blank string, but it needs
    # to be typed as a character array
    print MPIFFD "       CHARACTER*1 MPI_ARGV_NULL(1)\n";

    #
    # Error Classes
    print MPIFFD "       INTEGER MPI_SUCCESS\n";
    print MPIFFD "       PARAMETER (MPI_SUCCESS=0)\n";
    foreach $key (keys(%mpidef)) {
        if ($key =~ /MPI_ERR_/) {
            &print_mpif_int( $key );
        }
    }
    # Predefined error handlers
    foreach $key (ERRORS_ARE_FATAL, ERRORS_RETURN) {
        &print_mpif_int( "MPI_$key" );
    }
    # Compare operations
    foreach $key (IDENT,CONGRUENT,SIMILAR,UNEQUAL) {
        &print_mpif_int( "MPI_$key" );
    }
    # Window flavor and model
    foreach $key (FLAVOR_CREATE,FLAVOR_ALLOCATE,FLAVOR_DYNAMIC,FLAVOR_SHARED,
                  SEPARATE, UNIFIED) {
        &print_mpif_int( "MPI_WIN_$key" );
    }
    # Collective operations
    foreach $key (MAX, MIN, SUM, PROD, LAND, BAND, LOR, BOR, LXOR, BXOR, MINLOC, MAXLOC, REPLACE, NO_OP ) {
        &print_mpif_int( "MPI_$key" );
    }
    # Objects
    foreach $key ('COMM_WORLD', 'COMM_SELF', 'GROUP_EMPTY', 'COMM_NULL', 'WIN_NULL', 'FILE_NULL', 'GROUP_NULL', 'OP_NULL', 'DATATYPE_NULL', 'REQUEST_NULL', 'ERRHANDLER_NULL', 'INFO_NULL', 'INFO_ENV' ) {
        &print_mpif_int( "MPI_$key" );
    }
    # Attributes
    foreach $key (TAG_UB, HOST, IO, WTIME_IS_GLOBAL, UNIVERSE_SIZE, LASTUSEDCODE, APPNUM, WIN_BASE, WIN_SIZE, WIN_DISP_UNIT, WIN_CREATE_FLAVOR, WIN_MODEL ) {
        # Special cast:  The Fortran versions of these attributes have
        # value 1 greater than the C versions
        $attrval = $mpidef{"MPI_$key"};
        print "$key is $attrval\n" if $debug;
        if ($attrval =~ /^0x/) { $attrval = hex $attrval; }
        $attrval++;
        $attrval = "0x" . sprintf "%x", $attrval;
        print "$key is now $attrval\n" if $debug;
        $mpidef{"MPI_$key"} = $attrval;
        &print_mpif_int( "MPI_$key" );
    }
    # String sizes
    # See MPI-2 2.6.2 and 4.12.9; the constants for string lengths are
    # defined as one less than the C/C++ version

    # Missing - max processor name!
    # Handle max processor name here.
    $mpidef{"MPI_MAX_PROCESSOR_NAME"} = "\@MPI_MAX_PROCESSOR_NAME\@";
    # Other maximum values
    foreach $key (MAX_ERROR_STRING, MAX_PORT_NAME,
                  MAX_OBJECT_NAME, MAX_INFO_KEY, MAX_INFO_VAL,
                  MAX_PROCESSOR_NAME, MAX_DATAREP_STRING,
                  MAX_LIBRARY_VERSION_STRING ) {
        &print_mpif_int( "MPI_$key", -1 );
    }

    # predefined constants
    print MPIFFD "       INTEGER MPI_UNDEFINED\n";
    print MPIFFD "       PARAMETER (MPI_UNDEFINED=$mpidef{'MPI_UNDEFINED'})\n";
    &print_mpif_int( "MPI_KEYVAL_INVALID" );
    foreach $key ('BSEND_OVERHEAD', 'PROC_NULL', 'ANY_SOURCE', 'ANY_TAG', 'ROOT') {
        &print_mpif_int( "MPI_$key" );
    }
    #
    # Topology types
    foreach $key (GRAPH, CART, DIST_GRAPH) {
        &print_mpif_int( "MPI_$key" );
    }
    #
    # version
    &print_mpif_int( "MPI_VERSION" );
    &print_mpif_int( "MPI_SUBVERSION" );

    # Special RMA values
    &print_mpif_int( "MPI_LOCK_EXCLUSIVE" );
    &print_mpif_int( "MPI_LOCK_SHARED" );
    #
    # Datatypes
    # These are determined and set at configure time
    foreach $key (COMPLEX, DOUBLE_COMPLEX, LOGICAL, REAL, DOUBLE_PRECISION, INTEGER, '2INTEGER', '2DOUBLE_PRECISION', '2REAL', CHARACTER) {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPI_$key\@)\n";
    }
    # Value of MPI_BYTE from top level configure!
    $mpidef{"MPI_BYTE"} = hex "0x4c00010d";
    foreach $key (BYTE, UB, LB, PACKED) {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPI_$key\@)\n";
    }
    #&print_mpif_int( "MPI_BYTE" );
    #&print_mpif_int( "MPI_UB" );
    #&print_mpif_int( "MPI_LB" );
    #&print_mpif_int( "MPI_PACKED" );

    # Optional types
    # Warning: Should these use \@MPI_$key\@, since the
    # C-version must also compute these?
    foreach $key (INTEGER1, INTEGER2, INTEGER4, INTEGER8, INTEGER16,
                  REAL4, REAL8, REAL16, COMPLEX8, COMPLEX16, COMPLEX32) {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPI_$key\@)\n";
    }
    #
    # Fortran 90 types
    # MPI_INTEGER_KIND added in MPI 2.2
    print MPIFFD "       INTEGER MPI_ADDRESS_KIND\n";
    print MPIFFD "       PARAMETER (MPI_ADDRESS_KIND=\@ADDRESS_KIND\@)\n";
    print MPIFFD "       INTEGER MPI_OFFSET_KIND\n";
    print MPIFFD "       PARAMETER (MPI_OFFSET_KIND=\@OFFSET_KIND\@)\n";
    print MPIFFD "       INTEGER MPI_COUNT_KIND\n";
    print MPIFFD "       PARAMETER (MPI_COUNT_KIND=\@COUNT_KIND\@)\n";
    print MPIFFD "       INTEGER MPI_INTEGER_KIND\n";
    print MPIFFD "       PARAMETER (MPI_INTEGER_KIND=\@INTEGER_KIND\@)\n";
    #
    # C Types.  Note that we need to convert the C hex constant
    # into a decimal constant for Fortran (there is no standard
    # for for hex constants in Fortran, and different compilers make
    # use of different extensions)
    foreach $key (CHAR, SIGNED_CHAR, UNSIGNED_CHAR, WCHAR, SHORT,
                  UNSIGNED_SHORT, INT, UNSIGNED, LONG, UNSIGNED_LONG,
                  FLOAT, DOUBLE, LONG_DOUBLE, LONG_LONG_INT,
                  UNSIGNED_LONG_LONG, LONG_LONG, FLOAT_INT, DOUBLE_INT,
                  LONG_INT, SHORT_INT, "2INT", LONG_DOUBLE_INT) {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPI_$key\@)\n";
    }
    # C types added in MPI 2.2
    foreach $key (INT8_T, INT16_T, INT32_T, INT64_T, UINT8_T, UINT16_T,
                  UINT32_T, UINT64_T, C_BOOL, C_FLOAT_COMPLEX, C_COMPLEX,
                  C_DOUBLE_COMPLEX, C_LONG_DOUBLE_COMPLEX) {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPI_$key\@)\n";
    }
    foreach $key (qw(AINT OFFSET COUNT)) {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPI_${key}_DATATYPE\@)\n";
    }
    # C types added in MPI 3.0
    foreach $key (qw(CXX_BOOL
                     CXX_FLOAT_COMPLEX
                     CXX_DOUBLE_COMPLEX
                     CXX_LONG_DOUBLE_COMPLEX))
    {
        print MPIFFD "       INTEGER MPI_$key\n";
        print MPIFFD "       PARAMETER (MPI_$key=\@F77_MPIR_$key\@)\n";
    }
    # Datatype combiners
    foreach $key (NAMED, DUP, CONTIGUOUS, VECTOR, HVECTOR_INTEGER, HVECTOR,
                  INDEXED, HINDEXED_INTEGER, HINDEXED, INDEXED_BLOCK,
                  STRUCT_INTEGER, STRUCT, SUBARRAY, DARRAY, F90_REAL,
                  F90_COMPLEX, F90_INTEGER, RESIZED, HINDEXED_BLOCK) {
        &print_mpif_int( "MPI_COMBINER_$key" );
    }
    # Typeclasses
    foreach $key (REAL, INTEGER, COMPLEX) {
        &print_mpif_int( "MPI_TYPECLASS_$key" );
    }

    # RMA Asserts
    foreach $mode (NOCHECK, NOSTORE, NOPUT, NOPRECEDE, NOSUCCEED) {
        &print_mpif_int( "MPI_MODE_$mode" );
    }

    # comm_split_types
    foreach $type (SHARED) {
        &print_mpif_int( "MPI_COMM_TYPE_$type" );
    }
    &print_mpif_int( "MPI_MESSAGE_NULL" );
    &print_mpif_int( "MPI_MESSAGE_NO_PROC" );

    # Thread values
    foreach my $threadlevel (SINGLE, FUNNELED, SERIALIZED, MULTIPLE) {
        &print_mpif_int( "MPI_THREAD_$threadlevel" );
    }

    # MPI-2 types: Files
    if ($build_io) {
        # Modes
        foreach $mode (RDONLY, RDWR, WRONLY, DELETE_ON_CLOSE, UNIQUE_OPEN,
                       CREATE, EXCL, APPEND, SEQUENTIAL) {
            &print_mpif_int( "MPI_MODE_$mode" );
        }
        # Seek
        foreach $dir (SET, CUR, END) {
            &print_mpif_int( "MPI_SEEK_$dir" );
        }
        # Order
        foreach $order (C, FORTRAN) {
            &print_mpif_int("MPI_ORDER_$order");
        }
        # direction
        foreach $distrib (BLOCK, CYCLIC, NONE, DFLT_DARG) {
            &print_mpif_int("MPI_DISTRIBUTE_$distrib");
        }
        &print_mpif_int( "MPI_DISPLACEMENT_CURRENT", 0,
                         "\@FORTRAN_MPI_OFFSET\@" );
    }
    #
    # Fortran08 capability
    foreach $f08feature (SUBARRAYS_SUPPORTED, ASYNC_PROTECTS_NONBLOCKING) {
        print MPIFFD "       LOGICAL MPI_$f08feature\n";
        print MPIFFD "       PARAMETER(MPI_$f08feature=.FALSE.)\n";
    }
    #
    # Finally, the special symbols
    print MPIFFD "       INTEGER MPI_BOTTOM, MPI_IN_PLACE, MPI_UNWEIGHTED(1)\n";
    print MPIFFD "       INTEGER MPI_WEIGHTS_EMPTY(1)\n";

    # And the external names.  This are necessary to
    # ensure that these are passed as routines, not implicitly-defined
    # variables
    print MPIFFD "       EXTERNAL MPI_DUP_FN, MPI_NULL_DELETE_FN, MPI_NULL_COPY_FN\n";
    # Note that pmpi_wtime can cause problems with some Fortran compilers
    # if the corresponding routines aren't available (even if not used)
    print MPIFFD "       EXTERNAL MPI_WTIME, MPI_WTICK\n";
    print MPIFFD "       EXTERNAL PMPI_WTIME, PMPI_WTICK\n";
    # Add the external names for the MPI-2 attribute functions
    print MPIFFD "       EXTERNAL MPI_COMM_DUP_FN, MPI_COMM_NULL_DELETE_FN\n";
    print MPIFFD "       EXTERNAL MPI_COMM_NULL_COPY_FN\n";
    print MPIFFD "       EXTERNAL MPI_WIN_DUP_FN, MPI_WIN_NULL_DELETE_FN\n";
    print MPIFFD "       EXTERNAL MPI_WIN_NULL_COPY_FN\n";
    print MPIFFD "       EXTERNAL MPI_TYPE_DUP_FN, MPI_TYPE_NULL_DELETE_FN\n";
    print MPIFFD "       EXTERNAL MPI_TYPE_NULL_COPY_FN\n";
    print MPIFFD "       EXTERNAL MPI_CONVERSION_FN_NULL\n";
    # the time/tick functions
    # Special option.  Some compilers (particularly IBM's xl compilers)
    # allow the user to change the definition of the datatypes, such as
    # making real 8 bytes and double precision 16.  To allow mpif.h
    # to be used with such compilers, those compilers allow the
    # use of the non-standard real*8 to force exactly 8 bytes.
    # WARNING: REAL*8 is not standard and must not be used here.
    # Instead, the top level configure (in mpich/configure) will
    # replace DOUBLE PRECISION with REAL*8 where the Fortran compiler
    # supports it.
    print MPIFFD "       DOUBLE PRECISION MPI_WTIME, MPI_WTICK\n";
    print MPIFFD "       DOUBLE PRECISION PMPI_WTIME, PMPI_WTICK\n";
    # We avoid adding the external declarations because some Fortran
    # compilers then insist on linking with the routines, even if
    # they are not used.  Combined with systems that do not have weak
    # symbols, and you can get some strange link failures.

    # When building the Fortran interface for Microsoft Windows, there
    # are some additional compiler directives needed
    # This provides a hook for any DLL import directives.  We need to
    # make this a configure-time variable because some compilers (in
    # particular, a version of the Intel Fortran compiler for Linux)
    # will read directives for other compilers and then flag as fatal
    # errors directives that it does not support but does recognize.
    print MPIFFD "\@DLLIMPORT\@\n";

    # Add the common blocks for the special constants

    # Use one common block for each MPI Fortran constant to avoid possible
    # alignment issue when different Fortran compilers are used in building
    # the MPI libraries and compiling/linking with the user application.
    # This does not eliminate the potential alignment warnings from the
    # linker. Since the Fortran77 binding only needs the pointer address
    # but never access the content of the pointer, so alignment warnings
    # should be harmless. Alignment warnings from linker will be addressed
    # by checking that Fortran common block alignment created in C is OK
    # by the Fortran compiler(done at configure time for the primary compilers)

    # Add the common block for the character parameter ARGVS_NULL (Fortran
    # requires character data in a different common block than
    # non-character data)

    print MPIFFD "\
       COMMON /MPIFCMB5/ MPI_UNWEIGHTED
       COMMON /MPIFCMB9/ MPI_WEIGHTS_EMPTY
       SAVE /MPIFCMB5/
       SAVE /MPIFCMB9/

       COMMON /MPIPRIV1/ MPI_BOTTOM, MPI_IN_PLACE, MPI_STATUS_IGNORE

       COMMON /MPIPRIV2/ MPI_STATUSES_IGNORE, MPI_ERRCODES_IGNORE
       SAVE /MPIPRIV1/,/MPIPRIV2/

       COMMON /MPIPRIVC/ MPI_ARGVS_NULL, MPI_ARGV_NULL
       SAVE   /MPIPRIVC/\n";

    close( MPIFFD );
    &ReplaceIfDifferent( "mpif.h.in", "mpif.h.in.new" );
} # if write_mpif

sub print_mpif_int {
    my $key = $_[0];
    my $value = $mpidef{$key};
    my $valueOffset = $_[1];
    my $inttype = $_[2];

    # integertype lets use change the integer type of the
    # variable; e.g., to make it integer*8 or integer (kind=MPI_OFFSET_KIND).
    # This is needed for MPI_DISPLACEMENT_CURRENT.
    # Because this will need to be set by configure, if set, this
    # needs to be a configure variable.
    my $integertype = "INTEGER";

    if (defined($inttype)) {
        $integertype = $inttype;
    }

    if (!defined($value) || $value eq "") {
        print STDERR "No value found for \"$key\"\n";
        return 0;
    }
    # Remove any casts
    print "Input value for $key = $value\n" if $debug;
    # Add a special case to for MPIX_*
    if ($value =~ /\(MPIX/) {
        $value =~ s/\(MPIX_[A-Za-z0-9]*\s*\)//;
        print "cast removal: $value\n" if $debug;
        # Remove any surrounding (MPI_REQUEST_NULL)
        if ($value =~ /\(\s*[A-Z_]*\)/) {
            $value =~ s/\(\s*([A-Z_]*)\s*\)/$1/;
            print "paren removal: $value\n" if $debug;
        }
    }
    if ($value =~ /\(MPI/) {
        $value =~ s/\(MPI_[A-Za-z0-9]*\s*\)//;
        print "cast removal: $value\n" if $debug;
    }
    # Remove any surrounding ()
    if ($value =~ /\(\s*[-a-fx0-9]*\)/) {
        $value =~ s/\(\s*([-a-fx0-9]*)\s*\)/$1/;
        print "paren removal: $value\n" if $debug;
    }
    # Convert hex to decimal
    if ($value =~ /^0x[a-f\d]*/) {
        $value = hex $value;
        print "hex conversion: $value\n" if $debug;
    }
    if (defined($valueOffset) && $valueOffset ne "0") {
        if ($value =~ /^-?\d+/) {
            $value += $valueOffset;
        }
        else {
            $value .= "$valueOffset";
        }
    }
    print MPIFFD "       $integertype $key\n";
    print MPIFFD "       PARAMETER ($key=$value)\n";
}

sub ReadInterfaceForDefinitions {
    my $prototype_file = $_[0];
    my $linecount = 0;

    open ( MPIFD, "<$prototype_file" ) || die "Could not open $prototype_file\n";
    #
    # First, find the values that we need
    while (<MPIFD>) {
        $linecount++;
        # Remove any comments; check for problems
        $origline = $_;
        while (/(.*)\/\*(.*?)\*\/(.*)/) {
            my $removed = $2;
            $_ = $1.$3;
            if ($2 =~ /\/\*/) {
                print STDERR "Error in processing comment within interface file $prototype_file in line $origline";
            }
        }

        # We should also skip #ifndef xxx, for some xxx.
        if (/^#\s*ifndef\s+(\w*)/) {
            $ndefname = $1;
            if (defined($skipBlocks{$ndefname})) {
                &SkipCPPIfdef( MPIFD );
            }
        }

        # Use \S instead of [^\s].  See the comment above
        if (/^\s*#\s*define\s+(MPI[X]*_[A-Za-z_0-9]*)\s+(\S+)(.*)/) {
            my $name      = $1;
            my $val       = $2;
            my $remainder = $3;
            print "Found definition of $name as $val\n" if $debug;
            # If the name has some lower case letters in it, we
            # need to skip it (e.g., for a define MPI_Comm_c2f...)
            if ($name =~ /[a-z]/) { next; }
            if (defined($mpidef{$name})) {
                # We want to catch the case ((cast) value).  In
                # The above definition, the space will break the
                # value into the cast (actually, "((cast)").
                $fullval = "$val $remainder";
                if ($fullval =~ /\(\(([^\(\)]*)\)\s*([^\(\)]*)\s*\)/) {
                    $val = "(($1)$2)";
                }
                if ($mpidef{$name} ne $val) {
                    my $found = "";
                    if (defined($mpidefFile{$name})) {
                        my $location = $mpidefFile{$name};
                        $found = " found in $location";
                    }
                    print STDERR "Attempting to redefine $name with a new value $val found in $prototype_file:$linecount,\nusing original value of $mpidef{$name}$found\n";
                }
            }
            else {
                $mpidef{$name} = $val;
                $mpidefFile{$name} = "$prototype_file:$linecount";
            }
        }
        elsif (/typedef\s+enum\s+[A-Za-z0-9_]*\s*{\s*(.*)/) {
            # Allow a named type
            # Eat until we find the closing right brace
            $enum_line = $1;
            while (! ($enum_line =~ /}/)) {
                my $newline = <MPIFD>;
                $newline =~ s/\r*\n//;
                $enum_line .= $newline;
                $linecount++;
            }
            print "Handling enum $enum_line...\n" if $debug;
            # Now process for names and values
            while (($enum_line =~ /\s*(MPI[X]*_[A-Z_0-9]*)\s*=\s*([a-fx0-9]*)(.*)/)) {
                $mpidef{$1} = $2;
                $mpidefFile{$1} = "$prototype_file:$linecount";
                $enum_line = $3;
                print "Defining $1 as $2\n" if $debug;
               }

           }
        elsif (/enum\s+([A-Za-z0-9_]*)\s*{\s*(.*)/) {
            # Allow a named type
            # Eat until we find the closing right brace
            my $enum_name = $1;
            my $enum_line = $2;
            while (! ($enum_line =~ /}/)) {
                print "reading for $enum_name...\n" if $debug;
                my $newline = <MPIFD>;
                $newline =~ s/\r*\n//;
                $enum_line .= $newline;
                $linecount++;
            }
            # Now process for names and values
            while (($enum_line =~ /\s*(MPI[X]*_[A-Z_0-9]*)\s*=\s*([a-fx0-9]*)(.*)/)) {
                my $name = $1;
                my $val = $2;
                my $remainder = $3;
                $mpidef{$name} = $val;
                $mpidefFile{$name} = "$prototype_file:$linecount";
                $enum_line = $remainder;
                print "Defining $name as $val\n" if $debug;
               }

           }
    }
    close (MPIFD);
}

#
# ISSUES NOT YET HANDLED
# ----------------------------------------------------------------------------
# Fortran Integer conversion.
# If C ints and Fortran integers are not the same size, we have to do
# more.  In the case of arrays, we must make temporary copies.
# In MPICH1, there is also code for the case where the sizes of
# the C and Fortran integers are not known.  Roughly, the code could look
# like
# #ifdef SIZEOF_F77_INTEGER = SIZEOF_INT
#   straight-forward code
# #else
# {
#   code that converts arrays, calls routine, frees arrays
# }
# #endif
#
# There are several options for allocating the temporary arrays
# For some, like cartesian dimension arrays, it is reasonable to
# use a predeclared array (and signal an error if too large)
# For the others, use a predeclared array with a special case
# for extra-large
#
# Scalars:
# FintToint_in_decl: int *vi$count;
# FintToint_in_arg: vi$count
# FintToint_ftoc: vi$count = (int)v$count
# similar for intToFint_out
# For arrays,
# FintTointArray_in_decl ...
#
# ----------------------------------------------------------------------------
# Character buffer handling for choice arguments
#  If Fortran passes character arrays as a pair of arguments (rather than
# putting the second argument at the end of the arg list), then all of the
# choice arg routines must check the *count* of the number of arguments,
# and then, if there are too many args, assume that the choice buffer
# is a character.  Note that for Sendrecv, there is no unique
# solution unless you know more about the MPI datatypes.
#
# ----------------------------------------------------------------------------
sub SkipCPPIfdef {
    my $FD = $_[0];
    my $depth = 1;

    while (<$FD>) {
        if (/^#\s*endif/) {
            $depth--;
            #print "Depth is now $depth\n";
        }
        elsif (/^#\s*if/) {
            $depth++;
            #print "Depth is now $depth\n";
        }
        #print "Skipping $_";
        if ($depth <= 0) { last; }
    }
    return 0;
}
# ---------------------------------------------------------------------------
#
# Replace old file with new file only if new file is different
# Otherwise, remove new filename
sub ReplaceIfDifferent {
    my ($oldfilename,$newfilename) = @_;
    my $rc = 1;
    if (-s $oldfilename) {
        $rc = system "cmp -s $newfilename $oldfilename";
        $rc >>= 8;   # Shift right to get exit status
    }
    if ($rc != 0) {
        # The files differ.  Replace the old file
        # with the new one
        if (-s $oldfilename) {
            print STDERR "Replacing $oldfilename\n";
            unlink $oldfilename;
        }
        else {
            print STDERR "Creating $oldfilename\n";
        }
        rename $newfilename, $oldfilename ||
            die "Could not replace $oldfilename";
    }
    else {
        unlink $newfilename;
    }
}
